
package com.tools.cleanmaster.functionpage;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;

import com.tools.cleanmaster.utils.UiUtils;
import com.tools.cleanmaster.utils.Utils;

public class CoolDownScanView extends View {

    private static final float SCAN_LINE_DISMISS_VALUE = 0.98f;

    /**
     * the duration of the scan anim
     */
    static final int DURATION = 4000;

    /**
     * the startDelay of the scan anim
     */
    private static final int STARTDELAY = 600;
    private static final int TRANS_ANIM_TIME = 3000;

    private static final float ANIMATORVALUE_DEFAULT = 0.00001f;

    private static final float SCAN_BOOTOM_RATIO = 0.84f;
    private static final float SCAN_TOP_RATIO = 0.06f;
    private static final int ACCURACY = 1024;
    private boolean mIsSupportAnima;

    private float mCenterX;
    private float mCenterY;

    private static float mRingRadius;
    private static float mPhoneRadius;
    private static float mRingLeftLine;
    private static float mRingTopLine;
    private static float mRingRightLine;
    private static float mRingBottomLine;

    private static float mPhoneLeftLine;
    private static float mPhoneTopLine;
    private static float mPhoneRightLine;
    private static float mPhoneBottomLine;

    private static Paint mPaint;
    private static Paint mScanRayPaint;
    private static float mLineStrokeWidth;

    private static RectF mCircuitBoardRect;
    private static RectF mCpuBoardRect = new RectF();
    private static RectF mCometRectF;
    private static Rect mComposeSrc = new Rect();
    private static RectF mComposeDst = new RectF();

    private Bitmap mPhoneWithCricuit;
    private Bitmap mScanRay;
    private Bitmap mPhone;
    private Bitmap mAbNorPhoneWithCricuit;
    private Bitmap mAbNorScanRay;
    private Bitmap mAbNorPhone;
    private boolean mIsNormal = true;
    private boolean mStateChange = false;
    private float mCometSweepAngle;
    private float mLineStartY;
    private static float mAnimatorValue;
    private static int mPhoneImgWidth;
    private static int mPhoneImgHeight;
    private static float mPhoneScanStartDrawLine;
    private static int mScanRayLineLeft;
    private ValueAnimator mTransStateAnim;
    private float mTransValue = 1;
    private boolean mNeedTransAnim;

    public class MyEaseCubicInterpolator implements Interpolator {

        private int mLastI = 0;
        private final PointF mControlPoint1 = new PointF();
        private final PointF mControlPoint2 = new PointF();

        /**
         * CubicInterpolator
         *
         * @param controlPointx1 the x coordinate of the controlPoint1
         * @param controlPointy1 the y coordinate of the controlPoint1
         * @param controlPointx2 the x coordinate of the controlPoint2
         * @param controlPointy2 the y coordinate of the controlPoint2
         */
        public MyEaseCubicInterpolator(float controlPointx1, float controlPointy1,
                                       float controlPointx2, float controlPointy2) {
            mControlPoint1.x = controlPointx1;
            mControlPoint1.y = controlPointy1;
            mControlPoint2.x = controlPointx2;
            mControlPoint2.y = controlPointy2;
        }

        @Override
        public float getInterpolation(float input) {
            float t = input;
            for (int i = mLastI; i < ACCURACY; i++) {
                t = 1.0f * i / ACCURACY;
                double x = cubicCurves(t, 0, mControlPoint1.x, mControlPoint2.x, 1);
                if (x >= input) {
                    mLastI = i;
                    break;
                }
            }

            double value = cubicCurves(t, 0, mControlPoint1.y, mControlPoint2.y, 1);
            if (value > 0.999d) {
                value = 1;
                mLastI = 0;
            }

            return (float) value;
        }

        /**
         * Four points p0, p1, p2 and p3 in the plane or in higher-dimensional
         * space define a cubic Bézier curve
         *
         * @param t  time ratio [0,1]
         * @param p0 the value of the start point
         * @param p1 the value of the controlPoint1
         * @param p2 the value of the controlPoint2
         * @param p3 the value of the end point
         */
        private double cubicCurves(double t, double p0, double p1, double p2, double p3) {
            double value;
            double u = 1 - t;

            value = Math.pow(u, 3) * p0 + 3 * Math.pow(u, 2) * t * p1
                    + 3 * u * Math.pow(t, 2) * p2 + Math.pow(t, 3) * p3;

            return value;
        }

    }

    public CoolDownScanView(Context context) {
        super(context);
        init(context);
    }

    public CoolDownScanView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }


    public CoolDownScanView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }



    private void init(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mScanRayPaint = new Paint();
        mScanRayPaint.setAntiAlias(true);
        mScanRayPaint.setColor(Color.WHITE);
        mIsSupportAnima = true;
        if (Build.VERSION.SDK_INT >= 11) {
            setLayerType(View.LAYER_TYPE_HARDWARE, null);
        }
        mCircuitBoardRect = new RectF(0, 0, 0, 0);
        mCometRectF = new RectF(0, 0, 0, 0);
        mCpuBoardRect = new RectF(0, 0, 0, 0);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        updateDimensions(w, h);
        calculateCoordinates(ANIMATORVALUE_DEFAULT);
    }

    private void updateDimensions(int width, int height) {
        mCenterX = width / 2.0f;
        mCenterY = height / 2.0f;

        mRingRadius = mCenterY - height / 10.0f;
        mRingLeftLine = mCenterX - mRingRadius;
        mRingTopLine = mCenterY - mRingRadius;
        mRingRightLine = mCenterX + mRingRadius;
        mRingBottomLine = mCenterY + mRingRadius;

        mPhoneImgWidth = getPhoneWithCircuit().getWidth();
        mPhoneImgHeight = getPhoneWithCircuit().getHeight();
        mPhoneLeftLine = mCenterX - mPhoneImgWidth / 2;
        mPhoneTopLine = mCenterY - mPhoneImgHeight / 2;
        mPhoneRightLine = mCenterX + mPhoneImgWidth / 2;
        mPhoneBottomLine = mCenterY + mPhoneImgHeight / 2;
        mLineStrokeWidth = getScanRay().getHeight();

        mCircuitBoardRect.set(mRingLeftLine, mRingTopLine, mRingRightLine, mRingBottomLine);
        mCometRectF.set(mRingLeftLine, mRingTopLine, mRingRightLine, mRingBottomLine);
        mCpuBoardRect.set(mPhoneLeftLine, mPhoneTopLine, mPhoneRightLine, mPhoneBottomLine);
        mPhoneRadius = (mPhoneImgHeight * SCAN_BOOTOM_RATIO + mLineStrokeWidth) / 2;
        mPhoneScanStartDrawLine = mPhoneTopLine - mLineStrokeWidth / 2 + mPhoneImgHeight * SCAN_TOP_RATIO;
        mScanRayLineLeft = (width - getScanRay().getWidth()) / 2;
        loadBitmaps();
    }


    private void loadBitmaps() {
        getPhone();
        getPhoneWithCircuit();
        getPhoneWithCircuit();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawScan(canvas);
    }

    private void drawScan(Canvas canvas) {
        if (mNeedTransAnim) {
            int normalAlpha = (int) (255 * (1 - mTransValue));
            int abnormalAlpha = (int) (255 * mTransValue);
            mPaint.setAlpha(abnormalAlpha);
            drawPhoneComposeBitmap(canvas, mAbNorPhone, mAbNorPhoneWithCricuit, mPaint);
            mPaint.setAlpha(normalAlpha);
            drawPhoneComposeBitmap(canvas, mPhone, mPhoneWithCricuit, mPaint);
            mPaint.setAlpha(abnormalAlpha);
            drawScanLine(canvas, mAbNorScanRay, mPaint);
            mPaint.setAlpha(normalAlpha);
            drawScanLine(canvas, mScanRay, mPaint);
        } else {
            drawPhoneComposeBitmap(canvas, mPhone, mPhoneWithCricuit, null);
            drawScanLine(canvas, mScanRay, null);
        }
    }

    private Bitmap getPhoneWithCircuit() {
        if (mPhoneWithCricuit == null) {
            mPhoneWithCricuit = Utils.decodeSampledBitmapFromResource(getResources(), com.tools.cleanmaster.R.drawable.cpu_normal_finish_scan_icon, UiUtils.dip2px(getContext(), 240), UiUtils.dip2px(getContext(), 240));
        }
        return mPhoneWithCricuit;
    }

    private Bitmap getPhone() {
        if (mPhone == null) {
            mPhone = Utils.decodeSampledBitmapFromResource(getResources(), com.tools.cleanmaster.R.drawable.cpu_normal_start_scan_icon, UiUtils.dip2px(getContext(), 240), UiUtils.dip2px(getContext(), 240));
        }
        return mPhone;
    }

    private void drawScanLine(Canvas canvas, Bitmap scanRay, Paint paint) {
        if (mIsSupportAnima && mAnimatorValue < SCAN_LINE_DISMISS_VALUE && scanRay != null) {
            canvas.drawBitmap(scanRay, mScanRayLineLeft, mLineStartY - mLineStrokeWidth / 2, paint);
        }
    }

    private void drawPhoneComposeBitmap(Canvas canvas, Bitmap cpuBoard, Bitmap thermometer, Paint paint) {
        if (cpuBoard != null && thermometer != null) {

            if (mLineStartY >= mPhoneBottomLine) {
                canvas.drawBitmap(cpuBoard, null, mCpuBoardRect, paint);
            } else if (mLineStartY < mPhoneBottomLine && mLineStartY > mPhoneTopLine) {
                int offset = (int) (mLineStartY - mPhoneTopLine);
                int width = cpuBoard.getWidth();
                int height = cpuBoard.getHeight();
                // 绘制上半区
                mComposeSrc.set(0, 0, width, offset);
                mComposeDst.set(mPhoneLeftLine, mPhoneTopLine, mPhoneRightLine, mLineStartY);
                canvas.drawBitmap(cpuBoard, mComposeSrc, mComposeDst, paint);
                // 绘制下半区
                mComposeSrc.set(0, offset, width, height);
                mComposeDst.set(mPhoneLeftLine, mLineStartY, mPhoneRightLine, mPhoneBottomLine);
                canvas.drawBitmap(thermometer, mComposeSrc, mComposeDst, paint);
            } else {
                canvas.drawBitmap(thermometer, null, mCpuBoardRect, paint);
            }

        }


    }

    private Bitmap getScanRay() {
        if (mScanRay == null) {
            mScanRay = BitmapFactory.decodeResource(getResources(), com.tools.cleanmaster.R.drawable.cpu_normal_line_scan_icon);
        }
        return mScanRay;
    }

    /**
     * 开始扫描动画
     */
    public void startScan(final AnimatorListenerAdapter listener) {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(DURATION);
        animator.setStartDelay(STARTDELAY);
        animator.setInterpolator(new MyEaseCubicInterpolator(0.58f, 0.8f, 0.74f, 0.8f));
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                mAnimatorValue = (Float) animator.getAnimatedValue();
                calculateCoordinates(mAnimatorValue);
                postInvalidate();
            }

        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                listener.onAnimationEnd(animator);
                if (mTransStateAnim != null && mTransStateAnim.isRunning()) {
                    mTransStateAnim.end();
                }
            }

            @Override
            public void onAnimationCancel(Animator animator) {
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });
        animator.start();
    }

    /**
     * 计算绘制所需的相关参数
     *
     * @param animatorValue 动画系数
     */
    private void calculateCoordinates(float animatorValue) {
        // 采用和原省电中相同的扫描时间的控制
        mCometSweepAngle = 360 * animatorValue;
        if (mCometSweepAngle >= 0 && mCometSweepAngle <= 60) {
            float ratio = mCometSweepAngle / 60.0f;
            float alfaOffset = 180 * ratio;
            double alfaOffsetRadians = Math.toRadians(alfaOffset);
            float offsetY = (float) Math.cos(alfaOffsetRadians) * mPhoneRadius;
            mLineStartY = mPhoneScanStartDrawLine + mPhoneRadius + offsetY;
        } else if (mCometSweepAngle >= 75 && mCometSweepAngle <= 165) {
            float ratio = (mCometSweepAngle - 75) / 90.0f;
            float alfaOffset = 180 * ratio;
            double alfaOffsetRadians = Math.toRadians(alfaOffset);
            float offsetY = (float) Math.cos(alfaOffsetRadians) * mPhoneRadius;
            mLineStartY = mPhoneScanStartDrawLine + mPhoneRadius - offsetY;
        } else if (mCometSweepAngle >= 195 && mCometSweepAngle <= 300) {
            float ratio = (mCometSweepAngle - 195) / 105.0f;
            float alfaOffset = 45 * ratio;
            double alfaOffsetRadians = Math.toRadians(alfaOffset);
            float offsetY = (float) Math.cos(alfaOffsetRadians) * mPhoneRadius;
            mLineStartY = mPhoneScanStartDrawLine + mPhoneRadius + offsetY;
        } else if (mCometSweepAngle > 300 && mCometSweepAngle <= 355) {
            float ratio = (mCometSweepAngle - 300) / 55.0f;
            float alfaOffset = 135 * ratio + 45;
            double alfaOffsetRadians = Math.toRadians(alfaOffset);
            float offsetY = (float) Math.cos(alfaOffsetRadians) * mPhoneRadius;
            mLineStartY = mPhoneScanStartDrawLine + mPhoneRadius + offsetY;
        }
    }

    /**
     * 该接口从正常到非正常将会执行相应的动画，反之不行
     *
     * @param state
     */
    public void setNormal(boolean state) {
        mStateChange = state != mIsNormal;
        mIsNormal = state;
        if (!mIsNormal && mStateChange) {
            performTransStateAnim();
        }
    }

    private void performTransStateAnim() {
        mTransStateAnim = ValueAnimator.ofFloat(0, 1);
        mTransStateAnim.setDuration(TRANS_ANIM_TIME);
        mTransStateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mTransValue = (Float) animation.getAnimatedValue();
            }
        });
        mTransStateAnim.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                swapBitmaps();
                mNeedTransAnim = false;
            }

            @Override
            public void onAnimationStart(Animator animation) {
                updateAbnormalBitmaps();
                mNeedTransAnim = true;
            }
        });
        mTransStateAnim.start();
    }

    private void updateAbnormalBitmaps() {
        mAbNorPhone = Utils.decodeSampledBitmapFromResource(getResources(), com.tools.cleanmaster.R.drawable.cpu_over_start_scan_icon, UiUtils.dip2px(getContext(), 240), UiUtils.dip2px(getContext(), 240));
        mAbNorPhoneWithCricuit = Utils.decodeSampledBitmapFromResource(getResources(), com.tools.cleanmaster.R.drawable.cpu_over_finish_scan_icon, UiUtils.dip2px(getContext(), 240), UiUtils.dip2px(getContext(), 240));
        mAbNorScanRay = BitmapFactory.decodeResource(getResources(), com.tools.cleanmaster.R.drawable.cpu_over_line_scan_icon);
    }

    private void swapBitmaps() {
        mPhone = mAbNorPhone;
        mPhoneWithCricuit = mAbNorPhoneWithCricuit;
        mScanRay = mAbNorScanRay;
        mAbNorPhone = null;
        mAbNorPhoneWithCricuit = null;
        mAbNorScanRay = null;
    }
}
